/*
   FieldVis - A Tool for Visualizing Astrophysical Magnetohydrodynamic Flow
   
   Copyright (C) 2006 - 2008     FieldVis Development Team
  
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
  
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
  
   You should have received a copy of the GNU General Public License along
   with this program; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
   File     : ConfigFile.cpp
   Author   : Blayne Field
   
   Purpose  : Implementation file for the ConfigFile class
*/

#include "ConfigFile.h"


ConfigFile::ConfigFile(const std::string &pFileName, const std::vector<std::string> &rc)
   : fileName(pFileName), resources(rc), streamlinesMoved(false),
     vfield(NULL), colorMap(NULL), tex(NULL), twistField(NULL),
     radiusField(NULL)
{
   Reload();
}

ConfigFile::~ConfigFile()
{
   if (colorMap)
      delete colorMap;

   if (tex)
      delete tex;

   // need to manually delete the tubes from vTube
   for (std::vector<Streamtube*>::iterator i = vTube.begin(); i != vTube.end(); ++i)
   {
      delete *i;
   }

   if (vfield)
      delete vfield;

   if (twistField)
      delete twistField;

   if (radiusField)
      delete radiusField;
}

void ConfigFile::SetBoxSize(float *x, float *y, float *z)
{
   *x = (float) vfield->X() / vfield->GetScaleFactor();
   *y = (float) vfield->Y() / vfield->GetScaleFactor();
   *z = (float) vfield->Z() / vfield->GetScaleFactor();
}

void ConfigFile::Reload()
{
   ReloadNameValuePairs();
   SetUpExternalFields();
   UpdatePaths();
}

// Re-reads the name/value pairs from the file, and then updates all
// of the affected variables
void ConfigFile::ReloadNameValuePairs()
{
   // re-read the name/value pairs into a new set of pairs  
   std::ifstream inFile(fileName.c_str());
   if ( ! inFile)
   {
      std::cerr << "Configuration file: " << fileName << " not found." << std::endl;
      exit(-1);
   }

   std::string name, value;
   while (!inFile.eof())
   {
      inFile >> name >> value;
      pairs[name] = value;   
   }
   inFile.close();

   // check each filename specific item in the config file and reassign it to the
   // absolute path using the current working directory and resources.
   std::string configFileFullPath = GetFullPathToFile(fileName);
   std::string cwdPath = configFileFullPath.substr(0, configFileFullPath.find_last_of('/'));
   
   pairs["integration_x_field"] = GetFullPathToFileCheckRC(pairs["integration_x_field"], cwdPath, resources);
   pairs["integration_y_field"] = GetFullPathToFileCheckRC(pairs["integration_y_field"], cwdPath, resources);
   pairs["integration_z_field"] = GetFullPathToFileCheckRC(pairs["integration_z_field"], cwdPath, resources);
   
   pairs["seed_point_file"] = GetFullPathToFileCheckRC(pairs["seed_point_file"], cwdPath, resources);
   
   pairs["tube_color_file"] = GetFullPathToFileCheckRC(pairs["tube_color_file"], cwdPath, resources);
   // currently unused
   // pairs["tube_radius_field"] = GetFullPathToFileCheckRC(pairs["tube_radius_field"], cwdPath, resources);
   
   // we only want the color field for a varying mode
   if (pairs["tube_color_mode"] == "varying")
   {
      pairs["tube_color_field"] = GetFullPathToFileCheckRC(pairs["tube_color_field"], cwdPath, resources);
   }

   pairs["tube_texture_file"] = GetFullPathToFileCheckRC(pairs["tube_texture_file"], cwdPath, resources);
   
   // we only want a twisting field file if we are in twisting mode
   if (pairs["tube_twisting_mode"] == "on")
   {
      pairs["tube_twisting_field"] = GetFullPathToFileCheckRC(pairs["tube_twisting_field"], cwdPath, resources);
   }
}

// sets up the fields for things such as color, radius, etc...
void ConfigFile::SetUpExternalFields()
{
   // load in the main vector field (to be used for integration)
   int xDim = GetIntFromAttribute("integration_x_dim");
   int yDim = GetIntFromAttribute("integration_y_dim");
   int zDim = GetIntFromAttribute("integration_z_dim");

   vfield = new VectorField(pairs["integration_x_field"], pairs["integration_y_field"],
                            pairs["integration_z_field"], xDim, yDim, zDim);

   seedPoints = Point::GetPointsFromFile(pairs["seed_point_file"]);

   // load in the color field 
   if (colorMap)
   {
      delete colorMap;
   }
   colorMap = new ColorMap(pairs["tube_color_file"]);
   if (pairs["tube_color_mode"] == "varying")
   {
      colorMap->AssociateField(pairs["tube_color_field"], xDim, yDim, zDim, 
                               GetFloatFromAttribute("color_min"),
                               GetFloatFromAttribute("color_max") );
   }

   // load in the radius field
   if (pairs["tube_radius_mode"] == "varying")
   {
      // this is currently unused
      // radiusField = new ScalarField(pairs["tube_radius_field"], xDim, yDim, zDim);
   }

   // load in the texture object -- create as necessary...
   if (tex)
   {
      delete tex;
   }
   tex = new Texture(pairs["tube_texture_file"]);
   if (pairs["tube_twisting_mode"] == "on")
   {
      twistField = new ScalarField(pairs["tube_twisting_field"], xDim, yDim, zDim);
      if (pairs["tube_texture_mode"] == "off")
      {
         std::cerr << "WARNING: tube_texture_mode must be on for twisting to be displayed"
         << std::endl;
      }
   }
   // scale the appropriate scalar fields
   // this stuff was down with the tube creation but i don't think it needs to be there...
   tex->SetRange(GetFloatFromAttribute("texture_mag_min"),
                 GetFloatFromAttribute("texture_mag_max"));
   tex->SetMinMaxFactor(GetFloatFromAttribute("texture_min_max_factor"));
   tex->SetTextureLength(GetFloatFromAttribute("texture_length"));
}

void ConfigFile::UpdatePaths()
{
   // delete the existing tubes if any
   for (std::vector<Streamtube*>::iterator i = vTube.begin(); i != vTube.end(); ++i)
   {
      delete *i;
   }
   vTube.clear();
   vTube.resize(seedPoints.size(), static_cast<Streamtube*>(0));
   
   // create the streamtubes
   std::vector<pthread_t> threadIDs;
   ConfigThreadInfo *pInfo = new ConfigThreadInfo[seedPoints.size()];

   for (int i = 0; i < (int)seedPoints.size(); i++)
   {
      pInfo[i].ID = i;
      pInfo[i].config = this;
      pInfo[i].tube = NULL;

      pthread_t ID;
      pthread_create(&ID, NULL, ConfigFile::CreateStreamtubeThread, (void*)&pInfo[i]);
      threadIDs.push_back(ID);
   }

   for (int i = 0; i < (int)threadIDs.size(); i++)
   {
//      pthread_join(threadIDs[i], (void**)&vTube[i]);
      pthread_join(threadIDs[i], NULL);
   }
   for (int i = 0; i < (int)threadIDs.size(); i++)
   {
      vTube[i] = pInfo[i].tube;
   }

   delete [] pInfo;
}

// the thread stub...
void *ConfigFile::CreateStreamtubeThread(void *arg)
{
   ConfigThreadInfo *info = (ConfigThreadInfo*)arg;

   info->tube = info->config->CreateStreamtube(info->ID);

   pthread_exit(info->tube);
}

Streamtube *ConfigFile::CreateStreamtube(const int ID)
{
   Streamtube *newTube = new Streamtube(  vfield,
                                          seedPoints[ID],
                                          GetIntFromAttribute("integration_num_points"),
                                          (pairs["integration_direction"] == "both"),
                                          GetFloatFromAttribute("integration_minimum_threshold"),
                                          ID,
                                          colorMap,
                                          GetIntFromAttribute("tube_sides"),
                                          tex,
                                          (pairs["tube_texture_mode"] == "proportional"),
                                          twistField,
                                          GetFloatFromAttribute("tube_radius"));

   return(newTube);
}

// returns the point index of the seed point
int ConfigFile::MoveStreamlineSeed(int path, Point point)
{
   seedPoints[path] = point * vfield->GetScaleFactor();

   if (vfield->InBounds(seedPoints[path]))
   {
      streamlinesMoved = true;
      if (vTube[path] != NULL)
      {
         vTube[path]->PutTubeAt(seedPoints[path]);
      }
   }
   
   return vTube[path]->GetPathStartPointIndex();
}

Point ConfigFile::GetStreamlinePoint(int tubeIndex, int pointIndex) 
{
   return vTube[tubeIndex]->GetPathCoordinates(pointIndex);
}

void ConfigFile::WriteSeedsToFile()
{
   std::stringstream ss;
   ss << pairs["seed_point_file"] << time(NULL);
   std::string newFileNameString;
   ss >> newFileNameString;
   std::ofstream seedPointFile(newFileNameString.c_str());

   int points = seedPoints.size();
   for (int i = 0; i < points; i++)
   {
      seedPointFile << seedPoints[i];
   }
   seedPointFile.close();
}

void ConfigFile::AddStreamlineSeed(Point point)
{
   seedPoints.push_back(point * vfield->GetScaleFactor());

   UpdatePaths();
}

void ConfigFile::RemoveStreamlineSeed(int index)
{
   seedPoints.erase(seedPoints.begin()+index, seedPoints.begin()+index+1);

   UpdatePaths();
}

void ConfigFile::DrawStreamtubes(GLenum mode)
{
   for (std::vector<Streamtube*>::iterator i = vTube.begin(); i != vTube.end(); ++i)
   {
      (*i)->DrawStreamtube(mode);
   }
}

